← Blog

On-Device Function Calling on Android: Safe Local Actions

Build Android function calling with explicit schemas, deterministic validation, permission gates, confirmations, execution logs, and offline tests.

On-device function calling on Android lets a local model propose structured actions that the app can validate and execute. The model should never receive direct authority over Android APIs. It produces a typed proposal; application code checks the schema, permissions, current state, user intent, and risk policy before anything happens. This separation keeps model uncertainty away from destructive or privacy-sensitive operations.

Function calling is a protocol, not a permission

A function schema describes an action name, its arguments, and the fields required for a valid proposal. The model chooses a function and fills arguments. The application remains responsible for deciding whether the proposal is valid and allowed.

Use three distinct objects:

  1. Function definition: what the model is allowed to propose.
  2. Function proposal: the model’s structured output.
  3. Execution result: what application code actually did.

Do not collapse proposal and execution into one callback. Logging them separately makes it possible to see whether an incorrect outcome came from model selection, argument generation, policy, or the Android operation itself.

Start with a narrow allowlist

Good first functions are reversible, easy to validate, and limited to application-owned data. Examples include opening a screen, selecting a saved item, applying a filter, or creating a draft that still requires confirmation.

Avoid beginning with broad functions such as run_command, send_message, delete_file, or make_purchase. A generic function gives the model a large hidden action space and moves validation into unstructured strings.

Prefer this:

{
  "name": "create_note_draft",
  "arguments": {
    "title": "Project follow-up",
    "body": "Draft text"
  }
}

over this:

{
  "name": "execute",
  "arguments": {
    "instruction": "Create a note and send it to everyone"
  }
}

The first proposal can be validated field by field. The second hides multiple actions and recipients inside text.

The official mobile-actions pattern

Google’s FunctionGemma mobile actions guide demonstrates a workflow that defines a tool schema, fine-tunes a small function-calling model, converts it to a LiteRT-LM package, and loads it in Google AI Edge Gallery for on-device action testing. The important architectural lesson is the separation between model output and the functions available in the host application.

The guide is evidence that an on-device function-calling workflow exists. It is not evidence that every Android API, arbitrary third-party app, or unsupported model can be controlled safely. Your application still needs its own schemas, dataset, evaluator, and enforcement layer.

Validation before execution

Every proposal should pass a deterministic validator that the model cannot bypass.

Validate at least:

  • function name is in the current allowlist;
  • JSON or structured output parses completely;
  • no unknown argument is present;
  • all required arguments exist;
  • strings satisfy length and character rules;
  • enum values match known application values;
  • identifiers resolve to objects visible to the user;
  • the current screen and app state allow the action;
  • Android permissions are already granted or requested through normal UI;
  • risk policy permits automatic execution.

Invalid proposals should become a typed validation error. Do not “repair” a dangerous proposal by guessing what the model meant.

Confirmation and risk tiers

Assign each function a risk tier independent of the model’s confidence.

TierExampleExecution policy
Read-onlySearch local notesMay run after validation
Reversible local changeCreate a draft or change a filterRun, then show undo
External communicationSend a message or share a fileRequire explicit confirmation
Destructive or financialDelete data or purchaseStrong confirmation and additional safeguards

The model should not decide whether confirmation is necessary. That decision belongs to static application policy based on the selected function and current arguments.

For confirmation UI, show the concrete effect: recipient, record, amount, destination, or permission. “Allow AI action?” is too abstract for informed consent.

Android permissions remain Android permissions

Function calling does not replace the platform permission model. If a proposed action needs microphone, contacts, location, notifications, or storage access, request it through the normal Android flow and explain why the user-facing feature needs it.

Do not expose a function only because the permission has previously been granted. The function should also be relevant to the current product context and user request. A note assistant with microphone permission does not automatically need a “record anytime” function.

Local execution and privacy

When function selection is inferred on-device, the user command does not need to be transmitted to a remote inference server. That removes one content transmission step, but it does not prove the function itself is offline or private. A local model may propose opening a URL, calling a remote API, or sharing data through another app.

Classify both layers:

  • Inference boundary: where the command is interpreted.
  • Action boundary: where the selected function sends or changes data.

An action that crosses the device boundary should say so before confirmation. A product should not market a workflow as “fully local” merely because function selection happened locally.

Execution receipts and audit logs

Store a compact receipt for every proposal:

  • timestamp;
  • model artifact and runtime revision;
  • proposed function and redacted arguments;
  • validation result;
  • confirmation result;
  • execution result;
  • undo or recovery result.

Do not store sensitive argument values merely for debugging. Use identifiers, hashes, or redacted summaries where possible. Give users a visible activity history for actions that change state.

Receipts make regression tests possible. When a prompt or model changes, replay redacted commands and compare proposals before the new version reaches users.

Evaluation set

Build three groups of commands:

  1. commands that should select one known function;
  2. ambiguous commands that should ask a question;
  3. commands that should be refused because no allowed function applies.

Evaluation should check function choice, argument validity, unnecessary action, missed confirmation, and safe refusal. A model that selects the right function but invents an identifier has still failed.

Include adversarial text inside user-controlled content. A note title or document may contain instructions that should never become function arguments. Treat retrieved and displayed content as data, not higher-priority commands.

Release checklist

  • Every function has a typed schema and owner.
  • The allowlist is generated by app state, not a universal list.
  • Unknown functions and arguments fail closed.
  • Risk tier is static application policy.
  • External, destructive, and financial actions require confirmation.
  • Android permissions use normal platform UI.
  • Local inference and action network behavior are documented separately.
  • Execution receipts avoid unnecessary sensitive content.
  • The evaluator includes ambiguity, refusal, and adversarial cases.
  • Model and prompt updates replay the evaluation set.
  • Every state-changing action has recovery or an explicit reason it cannot.

Read on-device AI vs cloud AI for routing boundaries and how on-device AI works for the model execution foundation.

Last reviewed: 2026-09-16.