android

package
v2.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: GPL-3.0 Imports: 41 Imported by: 0

Documentation

Overview

Package android is the Android platform. It is ordinary Go: everything that needs the Android framework goes through the Host the embedding host supplies.

Index

Constants

View Source
const (

	// StrategyFilesystemPath hands the target a transient filesystem path.
	StrategyFilesystemPath = "filesystem_path"
	// StrategyContentURI hands the target a content URI with a read grant.
	StrategyContentURI = "content_uri"
	// StrategyApp starts an installed app with no media at all. The optional
	// literal extras select one profile-defined variant of it.
	StrategyApp = "app"
)

Variables

View Source
var ErrAppIdentity = errors.New("invalid android app identity")

ErrAppIdentity is returned for a virtual path that is not a launchable Android app identity.

View Source
var ErrLaunchDefinition = errors.New("invalid or unsupported launch definition")

ErrLaunchDefinition reports a launch definition that is malformed or asks for a capability this platform does not support.

Functions

This section is empty.

Types

type AppIdentity

type AppIdentity struct {
	Package string
	Variant string
	Name    string
}

AppIdentity is an installed app, optionally one profile-defined variant of it. It is identity only: the activity, action and extras that actually start the app live in the catalog, so an app that renames an activity does not invalidate identities already written to a card.

func ParseAppPath

func ParseAppPath(path string) (AppIdentity, error)

ParseAppPath reads an "android://" virtual path back into an identity.

func (AppIdentity) AppPath

func (a AppIdentity) AppPath() string

AppPath renders the identity as "android://<package>[:<variant>]/<Name>".

type AppInfo

type AppInfo struct {
	Package  string
	Activity string
	Label    string
	IsGame   bool
}

AppInfo is one launchable app the host found. Label is the app's own display name, which the host reads from the platform, never Core. IsGame is the platform's own game classification (Android's app category, or the legacy is-game flag), used to keep a generic app out of a synced game library: see installedAppsLauncherFor.

type DeviceStatus added in v2.20.0

type DeviceStatus struct {
	Power     *power.Detail
	Network   *hoststatus.Network
	Bluetooth *hoststatus.Bluetooth
	Display   *hoststatus.Display
	System    *hoststatus.System
	// Storage and Controllers are nil when the host does not report them; an
	// empty, non-nil slice is a report that there are none.
	Storage     []hoststatus.Volume
	Controllers []hoststatus.Controller
}

DeviceStatus is the embedding host's report of the device's state. A nil section is one the host does not report, and reads as unsupported.

type DispatchReceipt

type DispatchReceipt struct {
	Package  string
	Activity string
	Strategy string
}

DispatchReceipt names the component the host actually started, so the platform can confirm it is the one the definition asked for.

type FailureReason

type FailureReason string

FailureReason is a stable code for why the host could not inspect or start a launch target. It carries no user-facing text.

const (
	// FailureHostUnavailable means the host could not be reached at all.
	FailureHostUnavailable FailureReason = "host-unavailable"
	// FailureInvalidResponse means the host answered with something malformed.
	FailureInvalidResponse FailureReason = "invalid-response"
	// FailureNotInstalled means the target package is not installed.
	FailureNotInstalled FailureReason = "not-installed"
	// FailureActivityUnavailable means the package lacks a launchable target activity.
	FailureActivityUnavailable FailureReason = "activity-unavailable"
	// FailureStorageDenied means the target app has no storage permission.
	FailureStorageDenied FailureReason = "storage-denied"
	// FailureStorageVersion means the target build's storage access cannot be verified.
	FailureStorageVersion FailureReason = "storage-version"
	// FailureProviderUnsupported means the document provider cannot yield a local file.
	FailureProviderUnsupported FailureReason = "provider-unsupported"
	// FailureStorageUnmounted means the volume holding the media is not mounted.
	FailureStorageUnmounted FailureReason = "storage-unmounted"
	// FailureSourceUnavailable means the media file cannot be reached in its media folder.
	FailureSourceUnavailable FailureReason = "source-unavailable"
	// FailureSourceRevoked means the host's own access to the media's source
	// folder was revoked or withdrawn, distinct from FailureSourceUnavailable's
	// unspecified cause: the user can act on this one by re-granting access.
	FailureSourceRevoked FailureReason = "source-revoked"
	// FailureForegroundRequired means the host UI must be in front to start an activity.
	FailureForegroundRequired FailureReason = "foreground-required"
	// FailureCancelled means the dispatch was cancelled before it started.
	FailureCancelled FailureReason = "cancelled"
	// FailureOutcomeUnknown means the host cannot tell whether the target started.
	FailureOutcomeUnknown FailureReason = "outcome-unknown"
	// FailureRefused covers every other refusal.
	FailureRefused FailureReason = "refused"
)

type ForegroundState

type ForegroundState struct {
	BootID      string
	Permission  string
	SampledMs   int64
	ElapsedMs   int64
	Version     int
	Interactive bool
	Unlocked    bool
}

ForegroundState is a host-owned snapshot taken before intent dispatch or a reconciliation pass. It also supplies the boot and elapsed clocks needed to reject cross-boot replay.

type Host

type Host interface {
	// InspectTarget reports whether the definition's package and activity are
	// installed and launchable. A nil error means they are; a *HostError says
	// why not.
	InspectTarget(definition *LaunchDefinition) error
	// InstalledCores lists the launcher core files the host found. scanned is
	// false when the host has not looked, which is not evidence of absence.
	InstalledCores() (files []string, scanned bool)
	// MediaFolders returns the references of the media folders Core may
	// index now. A folder whose grant was revoked is not listed.
	MediaFolders(ctx context.Context) ([]string, error)
	// ReadMediaDir lists the directory at segments below the folder named by
	// reference; no segments lists the folder itself.
	ReadMediaDir(ctx context.Context, reference string, segments []string) ([]platforms.SourceEntry, error)
	// ReadFile returns up to limit+1 bytes of the file at segments below the
	// folder named by reference, so an oversized file is detectable. It is
	// used only at launch, to read the small amount of a media file's own
	// content a launch needs; indexing never reads a file's content.
	ReadFile(ctx context.Context, reference string, segments []string, limit int64) ([]byte, error)
	// Dispatch starts a validated definition for the file at segments below
	// the folder named by reference. Cancelling ctx abandons a dispatch in
	// flight. A *HostError says why the host refused.
	Dispatch(
		ctx context.Context,
		definition *LaunchDefinition,
		reference string,
		segments []string,
	) (DispatchReceipt, error)
	// InstalledApps lists the launchable apps the host found. scanned is false
	// when the host has not looked, which is not evidence of absence.
	InstalledApps() (apps []AppInfo, scanned bool)
	// AppIcon returns a readable, app-private PNG for a launchable package.
	// An unavailable icon is not evidence that the app is absent.
	AppIcon(packageName string) (path string, err error)
	// DispatchApp starts a definition that carries no media. A *HostError says
	// why the host refused.
	DispatchApp(definition *LaunchDefinition) (DispatchReceipt, error)
	// ForegroundState reads a fresh framework snapshot: boot identity, Usage
	// Access permission, and screen/keyguard state, on both the wall and
	// elapsed clocks. It is read fresh before every dispatch and
	// reconciliation pass; the platform never caches it.
	ForegroundState() (ForegroundState, error)
	// ForegroundEvents queries a bounded window of privacy-filtered
	// foreground evidence for one already-dispatched launch's whole
	// package - never a specific activity, so an intent that forwards
	// through more than one activity of the same app still reads as one
	// session. Cancelling ctx abandons a query in flight.
	ForegroundEvents(
		ctx context.Context, launchID, target string, fromMs, toMs int64,
	) (database.ForegroundEvidence, error)
	// Interactive cheaply reports whether the display is interactive right
	// now, with no Usage Access query or boot/elapsed-clock sampling — unlike
	// ForegroundState, this is meant to be called often by a background
	// loop, never as session-timing evidence. See platforms.InteractivityReader.
	Interactive() bool
}

Host is the embedding host's authority over the Android framework. The platform never touches packages, intents or document providers itself. Implementations must be safe for concurrent use.

Media folders the user granted to the host are named by the host's own reference for each, which Core treats as opaque. Core turns references into source roots and never shows or stores one.

type HostError

type HostError struct {
	Reason FailureReason
}

HostError is the typed failure a Host returns. Any other error from a Host is treated as FailureHostUnavailable.

func (*HostError) Error

func (e *HostError) Error() string

type HostReturn

type HostReturn struct {
	// ObserverStartedElapsedMs is when the UI process now reporting started.
	// A launch dispatched before it came from a process that died, so no
	// return can ever be observed for it. Zero when unknown.
	ObserverStartedElapsedMs int64
	// ReturnedWallMs and ReturnedElapsedMs are the earliest launcher resume
	// the host has not yet delivered. Zero when there was none.
	ReturnedWallMs    int64
	ReturnedElapsedMs int64
}

HostReturn is what the host observed of its own launcher UI, all from one boot: the UI process cannot outlive a reboot.

type LaunchDefinition

type LaunchDefinition struct {
	ID            string `json:"id"`
	Name          string `json:"name,omitempty"`
	Variant       string `json:"variant,omitempty"`
	System        string `json:"system"`
	Package       string `json:"package"`
	Activity      string `json:"activity"`
	Action        string `json:"action"`
	Strategy      string `json:"strategy"`
	StorageAccess string `json:"storageAccess"`
	Repair        string `json:"repair"`
	DataSource    string `json:"dataSource,omitempty"`
	// Data is an Intent data URI, set only at dispatch from a file the
	// definition itself carries no reference to. Reserved for the one
	// package whose exported activity reads its target from Intent data
	// (scummVMPackage); every other definition must leave it empty.
	Data         string        `json:"data,omitempty"`
	Extensions   []string      `json:"extensions"`
	Extras       []LaunchExtra `json:"extras,omitempty"`
	Version      int           `json:"version"`
	MaxTargetSDK int           `json:"maxTargetSdk,omitempty"`
	GrantReadURI bool          `json:"grantReadUri,omitempty"`
	ClipData     bool          `json:"clipData,omitempty"`
}

LaunchDefinition is binding data: which component to start and how media reaches it. Version 1 carries a transient filesystem path. Version 2 carries a content URI with an explicit read grant and ClipData. A host must never silently substitute one strategy for the other.

func (*LaunchDefinition) Validate

func (d *LaunchDefinition) Validate() error

Validate reports ErrLaunchDefinition unless every field is bounded and the definition uses exactly one supported strategy.

type LaunchExtra

type LaunchExtra struct {
	Name   string `json:"name"`
	Type   string `json:"type"`
	Source string `json:"source"`
	Suffix string `json:"suffix,omitempty"`
	Value  string `json:"value,omitempty"`
}

LaunchExtra binds one typed intent extra to a host capability, not a string template. Extras are string unless a package's own intent contract needs otherwise; adding a type for general use requires host negotiation.

type Platform

type Platform struct {
	// contains filtered or unexported fields
}

Platform implements platforms.Platform for Android. Token readers, input and screenshots have no host capability yet and report ErrNotSupported.

func New

func New(settings platforms.Settings, host Host) (*Platform, error)

New builds the platform over the directories the host owns. A nil host yields a platform with no launchers and no media.

func (*Platform) ConsoleManager

func (*Platform) ConsoleManager() platforms.ConsoleManager

func (*Platform) DeviceBluetooth added in v2.20.0

func (p *Platform) DeviceBluetooth() (hoststatus.Bluetooth, error)

func (*Platform) DeviceControllers added in v2.20.0

func (p *Platform) DeviceControllers() ([]hoststatus.Controller, error)

func (*Platform) DeviceDisplay added in v2.20.0

func (p *Platform) DeviceDisplay() (hoststatus.Display, error)

func (*Platform) DeviceNetwork added in v2.20.0

func (p *Platform) DeviceNetwork() (hoststatus.Network, error)

func (*Platform) DevicePower added in v2.20.0

func (p *Platform) DevicePower() (power.Detail, error)

func (*Platform) DeviceStorage added in v2.20.0

func (p *Platform) DeviceStorage() ([]hoststatus.Volume, error)

func (*Platform) DeviceSystem added in v2.20.0

func (p *Platform) DeviceSystem() (hoststatus.System, error)

func (*Platform) ForwardCmd

func (*Platform) ForwardCmd(*platforms.CmdEnv) (platforms.CmdResult, error)

func (*Platform) GamepadPress

func (*Platform) GamepadPress(string) error

func (*Platform) ID

func (*Platform) ID() string

func (*Platform) Interactive added in v2.20.0

func (p *Platform) Interactive() bool

Interactive reports whether the display is interactive right now, for platforms.InteractivityReader. A platform with no host yet defaults to interactive, the same as the normal, safe-by-default answer every other nil-host query on this type gives: never silently treat a background feature as ineligible just because the host has not connected yet.

func (*Platform) InvalidateHostSnapshot

func (p *Platform) InvalidateHostSnapshot()

InvalidateHostSnapshot drops the memoised host report so the next launcher listing or launch asks the host again. An embedding host calls it when packages are installed, removed or changed, and when the RetroArch core list changes.

func (*Platform) KeyboardPress

func (*Platform) KeyboardPress(string) error

func (*Platform) LaunchMedia

func (p *Platform) LaunchMedia(
	cfg *config.Instance,
	path string,
	launcher *platforms.Launcher,
	db *database.Database,
	options *platforms.LaunchOptions,
) error

LaunchMedia starts path with launcher, or with the launcher Core's usual inference picks when none was chosen upstream.

func (*Platform) LaunchSystem

func (*Platform) LaunchSystem(*config.Instance, string) error

func (*Platform) Launchers

func (p *Platform) Launchers(*config.Instance) []platforms.Launcher

Launchers lists the catalog in precedence order: within a system, the first launcher is the preferred one.

func (*Platform) LookupMapping

func (*Platform) LookupMapping(*tokens.Token) (string, bool)

func (*Platform) ManagedByPackageManager

func (*Platform) ManagedByPackageManager() bool

ManagedByPackageManager is true: the host's package is the only update path.

func (*Platform) PowerActions added in v2.20.0

PowerActions reports none: an application cannot reboot, shut down or suspend an Android device.

func (*Platform) PowerStatus added in v2.20.0

func (p *Platform) PowerStatus() (power.Status, error)

PowerStatus answers the update gate from the host's report. Until the host has reported, the charge is unknown, which the gate treats as unsafe.

func (*Platform) PreparePowerAction added in v2.20.0

func (*Platform) PreparePowerAction(context.Context, hoststatus.PowerAction) (func() error, error)

func (*Platform) ReadSourceDir

func (p *Platform) ReadSourceDir(ctx context.Context, path string) ([]platforms.SourceEntry, error)

ReadSourceDir lists a directory in one of the host's media folders.

func (*Platform) ReadSourceFile

func (p *Platform) ReadSourceFile(ctx context.Context, path string, limit int64) ([]byte, error)

ReadSourceFile returns up to limit+1 bytes of the file at path, so an oversized file is detectable. Unlike SourceRoots/ReadSourceDir, this is not part of platforms.SourceRootReader (indexing never calls it) - it satisfies the separate platforms.SourceFileReader capability instead, used at launch time by ScummVM/GameNative and to serve a scraped folder cover's bytes.

func (*Platform) ReconcileExternalSessions

func (p *Platform) ReconcileExternalSessions(ctx context.Context) error

ReconcileExternalSessions queries only recorded launch targets. The persisted cursor and store transaction make repeated host observations idempotent. It is run at startup and after every host return.

func (*Platform) ReconcileSessions

func (p *Platform) ReconcileSessions(ctx context.Context, observed HostReturn) error

ReconcileSessions runs after the frontend returns: it closes approximate launches from the observed return, then queries foreground evidence once it has settled.

func (*Platform) RefreshLauncherDependencies

func (p *Platform) RefreshLauncherDependencies() error

RefreshLauncherDependencies drops the memoised host report, so an explicit launchers.refresh sees a RetroArch core install without waiting out hostSnapshotTTL.

func (*Platform) ReturnToMenu

func (*Platform) ReturnToMenu() error

func (*Platform) RootDirs

func (*Platform) RootDirs(*config.Instance) []string

RootDirs is empty: media comes from the host's media folders, which Core indexes as source roots.

func (*Platform) ScanHook

func (*Platform) ScanHook(*tokens.Token) error

func (*Platform) Scrapers

func (p *Platform) Scrapers(*config.Instance) map[string]platforms.Scraper

Scrapers offers the libretro thumbnail scraper (box art, screenshots and title screens for indexed media, matched by libretro's own sanitised name; it never runs automatically after indexing, since it is the only scraper that downloads) and, once a host is present, an app icon scraper for installed apps offered as media.

func (*Platform) Screenshot

func (*Platform) Screenshot() (*platforms.ScreenshotResult, error)

func (*Platform) SetDeviceStatus added in v2.20.0

func (p *Platform) SetDeviceStatus(report *DeviceStatus)

SetDeviceStatus records the device state the embedding host observed. The host calls it whenever something changes; Core never looks for itself here, because only the host is allowed to ask the framework.

The network section's reachability is taken as the framework's own answer, so Core does not probe for it.

func (*Platform) SetDeviceStatusChanged added in v2.20.0

func (p *Platform) SetDeviceStatusChanged(onChange func())

SetDeviceStatusChanged registers the callback fired after each SetDeviceStatus.

func (*Platform) SetMediaHistoryHooks

func (p *Platform) SetMediaHistoryHooks(hooks platforms.MediaHistoryHooks)

SetMediaHistoryHooks receives the service's profile and history notification hooks. Android history for a LifecycleExternal launcher comes from session reconciliation, not the active-media tracker.

func (*Platform) SetTrackedProcess

func (*Platform) SetTrackedProcess(*os.Process)

func (*Platform) Settings

func (p *Platform) Settings() platforms.Settings

func (*Platform) SourceRoots

func (p *Platform) SourceRoots(ctx context.Context) ([]string, error)

SourceRoots lists the media folders the user granted to the host.

func (*Platform) StartPost

func (p *Platform) StartPost(
	ctx context.Context,
	_ *config.Instance,
	launcherContexts platforms.LauncherContextManager,
	_ func() *models.ActiveMedia,
	_ func(*models.ActiveMedia),
	db *database.Database,
	_ *idle.Scheduler,
) error

func (*Platform) StartPre

func (*Platform) StartPre(*config.Instance) error

func (*Platform) Stop

func (p *Platform) Stop() error

Stop drops the launcher contexts StartPost supplied. A host reuses one Platform across starts, and the manager from the previous run holds a cancelled context: leaving it in place makes launcherContext's readiness check pass with a dead context instead of refusing the launch.

func (*Platform) StopActiveLauncher

func (*Platform) StopActiveLauncher(platforms.StopIntent) error

func (*Platform) SupportedReaders

func (*Platform) SupportedReaders(*config.Instance) []readers.Reader

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL