portal

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Portal

Portal

This skill is for building Android apps that target Meta's Portal device family. Portal devices are discontinued (sales stopped end of 2022), but ADB is now enabled, so owners can sideload their own apps.
The hardware: Snapdragon-based Android tablets and TV sticks running a modified AOSP without Google Mobile Services. Several models, all touch or TV.
minSdkVersion
28 (Android 9) or 29 (Android 10) depending on device.
This skill pairs with metavr (Meta VR CLI) — install it first. Use
metavr adb
in place of raw
adb
everywhere. See
resources/hzdb.md
for the one-line install (via
npx
), the MCP-into-your-editor setup, and the Portal-relevant command surface. The full
metavr-cli
skill ships in the same repo and can also be loaded for deeper reference.
本技能适用于构建针对Meta Portal设备系列的Android应用。Portal设备已停产(2022年底停止销售),但ADB现已启用,设备所有者可以侧载自己的应用。
硬件信息:基于骁龙芯片的Android平板和电视棒,运行修改版AOSPGoogle移动服务(GMS)。包含多款型号,均支持触摸操作或为电视形态。不同设备对应的
minSdkVersion
为28(Android 9)或29(Android 10)。
本技能需搭配metavr(Meta VR命令行工具)使用——请先安装它。全程使用
metavr adb
替代原生
adb
。如需一键安装(通过
npx
)、编辑器集成MCP配置,以及Portal相关命令说明,请查看
resources/hzdb.md
。完整的
metavr-cli
技能位于同一仓库,也可加载以获取更详细参考。

Hard constraints — read before writing any code

硬性约束——编写代码前务必阅读

If you only remember a few things, remember these. Each is a class of bug Portal apps hit constantly.
  1. No Google Mobile Services. No Play Services, no Firebase, no FCM, no Play Billing, no Google Sign-In, no Google Maps SDK, no AdMob, no ML Kit. Apps with hard GMS deps will crash on launch. Pick non-GMS alternatives — see
    resources/app-requirements.md
    .
  2. minSdkVersion ≤ 28
    is required
    (Portal hardware tops out at API 29). For new apps,
    targetSdkVersion 29
    is the safest default. For porting existing apps,
    targetSdkVersion
    higher than 29 usually works fine — verified empirically with
    targetSdk = 36
    . Don't waste time downgrading the target SDK of an existing app unless you observe a concrete runtime issue.
  3. Launcher intent-filter is required. Touch devices need
    MAIN + LAUNCHER
    ; Portal TV needs
    MAIN + LEANBACK_LAUNCHER
    . The
    DEFAULT
    category is not required (verified empirically on Portal — apps with only
    MAIN + LAUNCHER
    appear on the home tile grid). Without one of these, the app installs but never appears on the home screen.
  4. App icon must include a PNG in
    mipmap-xxxhdpi/
    as a fallback. Declare
    android:icon
    (touch) or
    android:banner
    (TV) on the launcher activity. You can also ship adaptive icons (
    mipmap-anydpi-v26/
    ) and other density PNGs alongside — Portal's launcher correctly falls back to the
    mipmap-xxxhdpi
    PNG when the adaptive XML can't be rendered. Apps with only adaptive icons (no PNG fallback) will not have a visible icon on Portal.
  5. No contacts API. No account/credentials API.
    READ_CONTACTS
    is denied. The account provider returns nothing. 5a. Basic mic capture works; the far-field / beamformed mic array does not. Standard
    RECORD_AUDIO
    opens an
    AudioRecord
    stream and delivers real audio from
    handset-mic
    (the single-channel mic). The far-field beamformed array used by "Hey Portal" wake-word is gated by a Meta-signed native permission (
    com.facebook.alohasdk.permission.RECORD_AUDIO_PRIVILEGED
    ) and is not available to sideloaded apps. So basic voice features work; sideloaded wake-word detection and high-quality room-distance pickup do not. See
    resources/app-requirements.md
    § Microphone capture for details.
  6. Touch UI for tabletop, not phone form factor. Portal sits on a counter or stand. Users interact from 50–100 cm. Hit targets ≥ 64 dp (96 dp for primary actions), body text ≥ 16 sp (18 sp on Portal+), landscape-first. Full Portal design system (typography, spacing, palette, WCAG ratios, TalkBack rules) is in
    resources/design-guidelines.md
    ; for Jetpack Compose apps,
    resources/compose-theme.md
    is a copy-paste theme starter that bakes these rules into
    Color.kt
    /
    Theme.kt
    /
    Type.kt
    .
  7. Reserve the top ~64 dp (only if your top content sits within 64 dp of the canvas edge). Portal has a persistent system overlay strip at the top: back / home buttons (top-left) and Wi-Fi / status (top-right). It floats above app content with no automatic safe-area inset. Apps whose top UI hugs the edge (edge-to-edge toolbars, sticky headers, full-bleed modals) will tuck under it. Apps whose top content naturally sits ≥80 dp below the canvas edge don't need any change. The overlay pills are white, so apps with a light background in the top region need an additional dark scrim even after inset — see
    resources/app-requirements.md
    § Top system overlay.
如果您只能记住几点,请牢记以下内容。这些是Portal应用经常遇到的各类问题。
  1. 无Google移动服务(GMS)。不支持Play服务、Firebase、FCM、Play Billing、Google登录、Google Maps SDK、AdMob、ML Kit。依赖GMS的应用会在启动时崩溃。请选择非GMS替代方案——详见
    resources/app-requirements.md
  2. 必须满足
    minSdkVersion ≤ 28
    (Portal硬件最高支持API 29)。对于新应用
    targetSdkVersion 29
    是最安全的默认值。对于移植现有应用
    targetSdkVersion
    高于29通常也能正常运行——已通过
    targetSdk = 36
    验证。除非观察到具体运行时问题,否则无需浪费时间降级现有应用的目标SDK。
  3. 必须配置启动器intent-filter。触摸设备需要
    MAIN + LAUNCHER
    ;Portal TV需要
    MAIN + LEANBACK_LAUNCHER
    无需
    DEFAULT
    类别(已在Portal上验证——仅配置
    MAIN + LAUNCHER
    的应用会显示在主屏幕磁贴网格中)。如果缺少上述任一配置,应用会安装成功,但不会出现在主屏幕上。
  4. 应用图标必须包含
    mipmap-xxxhdpi/
    目录下的PNG作为回退
    。在启动器Activity上声明
    android:icon
    (触摸设备)或
    android:banner
    (电视设备)。您也可以同时提供自适应图标(
    mipmap-anydpi-v26/
    )和其他密度的PNG——当无法渲染自适应XML时,Portal启动器会正确回退到
    mipmap-xxxhdpi
    目录下的PNG。仅包含自适应图标(无PNG回退)的应用在Portal上不会显示可见图标。
  5. 无联系人API,无账户/凭证API
    READ_CONTACTS
    权限会被拒绝。账户提供器返回空值。 5a. 基础麦克风采集可用;远场/波束成形麦克风阵列不可用。标准
    RECORD_AUDIO
    权限可打开
    AudioRecord
    流,并从
    handset-mic
    (单声道麦克风)传输真实音频。“Hey Portal”唤醒词使用的远场波束成形阵列受Meta签名的原生权限(
    com.facebook.alohasdk.permission.RECORD_AUDIO_PRIVILEGED
    )限制,无法供侧载应用使用。因此基础语音功能可用;侧载应用无法实现唤醒词检测和高质量远距离拾音。详情请见
    resources/app-requirements.md
    的「麦克风采集」章节。
  6. 针对平板形态设计触摸UI,而非手机形态。Portal放置在柜台或支架上,用户从50–100厘米外交互。点击目标≥64 dp(主要操作≥96 dp),正文字体≥16 sp(Portal+上为18 sp),优先横向布局。完整的Portal设计系统(排版、间距、调色板、WCAG对比度、TalkBack规则)位于
    resources/design-guidelines.md
    ;对于Jetpack Compose应用,
    resources/compose-theme.md
    是可直接复制粘贴的主题模板,已将这些规则融入
    Color.kt
    /
    Theme.kt
    /
    Type.kt
  7. 预留顶部约64 dp空间(仅当您的顶部内容位于画布边缘64 dp范围内时需要)。Portal顶部有一个持久化系统覆盖条:返回/主页按钮(左上角)Wi-Fi/状态栏(右上角)。它悬浮在应用内容上方,且不会自动添加安全区域内边距。顶部UI紧贴边缘的应用(全屏工具栏、粘性标题、全屏模态框)会被覆盖条遮挡。顶部内容自然位于画布边缘下方≥80 dp的应用无需任何更改。覆盖条按钮为白色,因此顶部区域为浅色背景的应用即使添加了内边距,也需要额外添加深色遮罩——详见
    resources/app-requirements.md
    的「顶部系统覆盖条」章节。

Device matrix

设备矩阵

Device
minSdkVersion
Connection
Portal (1st and 2nd gen)28 / 29USB-C (back)
Portal Mini29USB-C (back)
Portal+ (1st and 2nd gen)28 / 29USB-C (back)
Portal Go29USB-C (under rubber cover on back)
Portal TV29USB-C
Set
minSdkVersion 28
if you want to cover everything. If you don't need to support 1st-gen Portal / Portal+, you can target 29.
设备
minSdkVersion
连接方式
Portal(第一代和第二代)28 / 29USB-C(背部)
Portal Mini29USB-C(背部)
Portal+(第一代和第二代)28 / 29USB-C(背部)
Portal Go29USB-C(背部橡胶盖下方)
Portal TV29USB-C
如果想覆盖所有设备,请设置
minSdkVersion 28
。如果无需支持第一代Portal/Portal+,可设置为29。

Quickstart

快速开始

Toolchain first — don't hand-hunt for it. Building needs two host-machine pieces: a JDK (Gradle/AGP run on it) and the Android SDK. Install the SDK with Google's
android
CLI
— don't go scavenging the filesystem for an SDK. For the JDK, point
JAVA_HOME
at Android Studio's bundled JBR if Android Studio is installed, otherwise install Temurin 17. The
android
CLI installs the SDK only — it does not provide a JDK. Full walkthrough:
resources/android-sdk-setup.md
.
bash
undefined
先配置工具链——不要手动查找。构建需要两台主机设备组件:JDK(Gradle/AGP运行依赖)和Android SDK。使用Google的
android
命令行工具
安装SDK——不要在文件系统中搜索SDK。对于JDK,如果已安装Android Studio,请将
JAVA_HOME
指向Android Studio捆绑的JBR;否则安装Temurin 17
android
命令行工具仅安装SDK——不提供JDK。完整教程:
resources/android-sdk-setup.md
bash
undefined

1) Get a JDK 17 (one-time, host machine — Gradle / AGP run on it; the
android
CLI does NOT install one)

1) 获取JDK 17(主机设备一次性操作——Gradle/AGP运行依赖;
android
命令行工具不包含JDK)

If Android Studio is installed, reuse its bundled JBR (no install needed):

如果已安装Android Studio,复用其捆绑的JBR(无需安装):

export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"

export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"

Otherwise install Temurin 17:

否则安装Temurin 17:

brew install --cask temurin@17 && export JAVA_HOME="$(/usr/libexec/java_home -v 17)"

brew install --cask temurin@17 && export JAVA_HOME="$(/usr/libexec/java_home -v 17)"

java -version # should report 17.x (or 21.x if using Android Studio JBR)
java -version # 应显示17.x(或使用Android Studio JBR时显示21.x)

2) Install Google's
android
CLI (it manages the SDK), then install the SDK packages

2) 安装Google的
android
命令行工具(用于管理SDK),然后安装SDK包

macOS: brew tap android/tap && brew install android-cli

macOS: brew tap android/tap && brew install android-cli

Any OS: curl -fsSL https://dl.google.com/android/cli/latest/darwin_arm64/install.sh | bash (see § 1 for other OSes)

任意系统: curl -fsSL https://dl.google.com/android/cli/latest/darwin_arm64/install.sh | bash (其他系统请查看第1章节)

Windows: winget install --id Google.AndroidCLI

Windows: winget install --id Google.AndroidCLI

android update # keep the CLI current
android update # 保持命令行工具为最新版本

Install the SDK. For a new project: API 28 + 29. For porting, also install whatever compileSdk needs (often 35/36).

安装SDK。新项目:API 28 + 29。移植项目还需安装compileSdk所需的版本(通常为35/36)。

android sdk install platforms/android-28 platforms/android-29 platform-tools build-tools/34.0.0 export ANDROID_HOME="$HOME/Library/Android/sdk" # macOS — Linux uses ~/Android/Sdk
android sdk install platforms/android-28 platforms/android-29 platform-tools build-tools/34.0.0 export ANDROID_HOME="$HOME/Library/Android/sdk" # macOS — Linux使用~/Android/Sdk

3) Install metavr (one-time, host machine — requires Node.js 20+)

3) 安装metavr(主机设备一次性操作——需要Node.js 20+)

See resources/hzdb.md for full details and MCP-into-your-editor setup.

完整详情和编辑器集成MCP配置请查看resources/hzdb.md。

npx -y metavr --version
npx -y metavr --version

(or install globally)

(或全局安装)

npm install -g @meta-quest/metavr
npm install -g @meta-quest/metavr

4) Enable ADB on the Portal

4) 在Portal上启用ADB

Portal: Settings → Debug → ADB Enabled. Enter PIN if prompted.

Portal: 设置 → 调试 → 启用ADB。如有提示,输入PIN码。

Connect USB-C. Tap "Allow" on the device the first time you connect.

连接USB-C。首次连接时在设备上点击“允许”。

5) Verify

5) 验证

metavr device list
metavr device list

Should list one Portal device. Example: 819PGF02P010SL23 device Portal aloha

应列出一台Portal设备。示例:819PGF02P010SL23 device Portal aloha

(or npx -y metavr device list)

(或使用npx -y metavr device list)

6) Build + install + launch

6) 构建 + 安装 + 启动

No ./gradlew in the project? Bootstrap the wrapper first — see resources/android-sdk-setup.md § 0a.

项目中无./gradlew?先初始化wrapper——详见resources/android-sdk-setup.md第0a章节。

./gradlew assembleDebug metavr app install -r app/build/outputs/apk/debug/app-debug.apk # -r/--replace reinstalls, keeping data metavr app launch com.example.myapp # or: metavr adb shell am start -n com.example.myapp/.MainActivity
./gradlew assembleDebug metavr app install -r app/build/outputs/apk/debug/app-debug.apk # -r/--replace 重新安装,保留数据 metavr app launch com.example.myapp # 或: metavr adb shell am start -n com.example.myapp/.MainActivity

(or use npx -y metavr instead of metavr if not globally installed)

(如未全局安装,使用npx -y metavr替代metavr)


If the device is missing from `metavr device list`, tap **ADB Enabled** on the Portal again — the toggle can race the USB connect.

如果`metavr device list`未显示设备,请再次点击Portal上的**启用ADB**——开关可能与USB连接冲突。

Build configuration

构建配置

app/build.gradle.kts
:
kotlin
android {
    compileSdk = 35

    defaultConfig {
        minSdk = 28
        targetSdk = 29
    }
}
compileSdk = 35
(or whatever's latest) is fine. The min/target are what matters for Portal compatibility.
app/build.gradle.kts
:
kotlin
android {
    compileSdk = 35

    defaultConfig {
        minSdk = 28
        targetSdk = 29
    }
}
compileSdk = 35
(或最新版本)没问题。min/target是影响Portal兼容性的关键。

Manifest

清单文件

The launcher entry varies by device family.
Touch devices (Portal, Portal Mini, Portal+, Portal Go):
xml
<activity
    android:name=".MainActivity"
    android:icon="@mipmap/ic_launcher"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
Portal TV (use
LEANBACK_LAUNCHER
and
android:banner
in place of
android:icon
):
xml
<activity
    android:name=".MainActivity"
    android:banner="@drawable/banner"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    </intent-filter>
</activity>
Without these, the app installs but is invisible. To support both families from one APK, declare both intent filters on the same activity.
启动器配置因设备系列而异。
触摸设备(Portal、Portal Mini、Portal+、Portal Go):
xml
<activity
    android:name=".MainActivity"
    android:icon="@mipmap/ic_launcher"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
Portal TV(使用
LEANBACK_LAUNCHER
android:banner
替代
android:icon
):
xml
<activity
    android:name=".MainActivity"
    android:banner="@drawable/banner"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    </intent-filter>
</activity>
缺少上述配置的话,应用会安装成功但不可见。如需通过一个APK支持两个设备系列,请在同一个Activity上声明两个intent-filter。

Permissions that work / don't work

可用/不可用权限

CapabilityStatus
CameraRegular
CAMERA
permission
MicrophoneRegular
RECORD_AUDIO
permission
SpeakerNo permission needed
BluetoothRegular
BLUETOOTH*
permissions
NetworkRegular
INTERNET
/
ACCESS_NETWORK_STATE
Touch / keyboard inputNo permission needed
Storage writeRegular
WRITE_EXTERNAL_STORAGE
(Android 9 model)
Storage delete (cross-app)App-owned only. Otherwise:
metavr adb shell rm
, or install a file-manager app
Contacts (
READ_CONTACTS
)
Not available. Denied at runtime
Device accounts (
AccountManager
)
Not available. Account provider returns nothing
功能状态
摄像头支持常规
CAMERA
权限
麦克风支持常规
RECORD_AUDIO
权限
扬声器无需权限
蓝牙支持常规
BLUETOOTH*
权限
网络支持常规
INTERNET
/
ACCESS_NETWORK_STATE
触摸/键盘输入无需权限
存储写入支持常规
WRITE_EXTERNAL_STORAGE
(Android 9机型)
存储删除(跨应用)仅支持应用自有文件。否则:使用
metavr adb shell rm
,或安装文件管理器应用
联系人(
READ_CONTACTS
不可用。运行时会被拒绝
设备账户(
AccountManager
不可用。账户提供器返回空值

Smart Camera SDK (binary pending)

Smart Camera SDK(二进制包待发布)

Portal's camera is driven by a system Smart Camera service that owns face/body tracking, framing, and auto-pan-zoom to keep people in frame. Apps do not drive the camera directly with
Camera2
to get that behavior — they request a session from the Smart Camera service and pass a
ModeSpec
(auto-frame, desk framing, meeting framing, fixed crop, etc.).
A standalone Smart Camera SDK is in development. Planned Gradle coordinate:
com.facebook.portal:smartcamera:1.1.+
. Until the binary ships, see
resources/smart-camera-sdk.md
for the API sketch — same shape, same
ModeSpec
modes, same
SmartCameraControlConnectionFactory
entry point.
Raw video frames are still available via the standard
Camera2
API; the Smart Camera SDK is for controlling the framing, not the pixels.
Portal的摄像头由系统级Smart Camera服务驱动,该服务负责面部/身体跟踪、取景,以及自动平移缩放以保持人物在画面中。应用无法直接使用
Camera2
驱动摄像头来实现上述行为——需向Smart Camera服务请求会话,并传递
ModeSpec
(自动取景、桌面取景、会议取景、固定裁剪等)。
独立的Smart Camera SDK正在开发中。计划的Gradle坐标:
com.facebook.portal:smartcamera:1.1.+
。在二进制包发布前,请查看
resources/smart-camera-sdk.md
获取API草图——形状一致,
ModeSpec
模式一致,
SmartCameraControlConnectionFactory
入口一致。
仍可通过标准
Camera2
API获取原始视频帧;Smart Camera SDK用于控制取景,而非像素处理。

Debug loop

调试流程

bash
metavr adb logcat                                  # full logcat
metavr adb logcat *:E                              # errors only
metavr adb logcat -s AndroidRuntime DEBUG libc     # crash signals
metavr log -c                                      # clear the log buffer (NOT `metavr adb logcat -c` — that flag is rejected)
metavr adb shell dumpsys activity activities       # what's running
metavr app clear com.example.myapp                 # wipe app data (or: metavr adb shell pm clear <pkg>)
metavr app uninstall com.example.myapp             # uninstall
metavr capture screenshot -o screen.png            # save a PNG of the screen
See
resources/debugging.md
for more patterns and common failure modes (icon missing, app invisible after install, app crashes on first launch, etc.).
bash
metavr adb logcat                                  # 完整日志
metavr adb logcat *:E                              # 仅错误日志
metavr adb logcat -s AndroidRuntime DEBUG libc     # 崩溃信号
metavr log -c                                      # 清空日志缓冲区(注意:不是`metavr adb logcat -c`——该参数会被拒绝)
metavr adb shell dumpsys activity activities       # 查看运行中的应用
metavr app clear com.example.myapp                 # 清除应用数据(或:metavr adb shell pm clear <pkg>)
metavr app uninstall com.example.myapp             # 卸载应用
metavr capture screenshot -o screen.png            # 保存屏幕截图为PNG
更多模式和常见故障(图标缺失、安装后应用不可见、首次启动崩溃等)请查看
resources/debugging.md

Resources

资源

  • resources/hzdb.md
    — what metavr is, one-line install, MCP-into-your-editor, Portal-relevant commands
  • resources/device-setup.md
    — full device prep walkthrough for a human user
  • resources/android-sdk-setup.md
    — install JDK 17, Android CLI, SDK platforms / build-tools
  • resources/native-toolchain.md
    — NDK / CMake / Ninja setup (use this when the project has native code; covers the deep-validation contract because
    source.properties
    alone isn't enough)
  • resources/app-requirements.md
    — manifest, icons, no-GMS, design checklist (deep)
  • resources/porting-existing-apps.md
    — playbook for taking an existing Android app and getting it onto Portal (no-GMS flavor, native code, common failures)
  • resources/design-guidelines.md
    — typography, spacing, color, accessibility, TV / D-pad, Smart Camera UX
  • resources/compose-theme.md
    — copy-paste Jetpack Compose theme starter (dark-forced theme, Portal palette, bundled-Inter typography, hit targets) with the no-GMS font fix
  • resources/smart-camera-sdk.md
    — Smart Camera API surface (binary pending)
  • resources/debugging.md
    metavr adb
    logcat / screenshot / dumpsys patterns
  • resources/sample-prompts.md
    — starter prompts to feed to Claude / Cursor / etc.
  • resources/hzdb.md
    —— metavr介绍、一键安装、编辑器集成MCP、Portal相关命令
  • resources/device-setup.md
    —— 面向普通用户的完整设备准备教程
  • resources/android-sdk-setup.md
    —— 安装JDK 17、Android CLI、SDK平台/构建工具
  • resources/native-toolchain.md
    —— NDK/CMake/Ninja配置(项目包含原生代码时使用;涵盖深度验证规则,因为仅
    source.properties
    不够)
  • resources/app-requirements.md
    —— 清单文件、图标、无GMS、设计检查清单(详细)
  • resources/porting-existing-apps.md
    —— 将现有Android应用移植到Portal的指南(无GMS版本、原生代码、常见故障)
  • resources/design-guidelines.md
    —— 排版、间距、颜色、无障碍、电视/方向键、Smart Camera UX
  • resources/compose-theme.md
    —— 可复制粘贴的Jetpack Compose主题模板(强制深色主题、Portal调色板、内置Inter字体、点击目标),包含无GMS字体修复
  • resources/smart-camera-sdk.md
    —— Smart Camera API接口(二进制包待发布)
  • resources/debugging.md
    ——
    metavr adb
    日志/截图/dumpsys模式
  • resources/sample-prompts.md
    —— 可用于Claude/Cursor等工具的初始提示词