Skip to content

Developer Tools8 min read

Submitting a calling app to Google Play in 2026 without losing your mind

Our calling app was ready for Play review, but two console-only forms kept the submit button locked: the foreground service declaration and Data safety. Here is the order of steps that worked.

In July 2026 we sent the Android build of ID Caller, our app for private audio and video calls without a phone number, to production review on Google Play. The AAB was signed, the listing was ready and the internal track had a working release, yet the submit button stayed locked. Two forms held it: the foreground service declaration and Data safety. Both live only in Play Console: the Play Developer API, which we use for listings, contact details, tracks and AAB uploads, does not expose them.

This is the guide we wanted that day. There are no screenshots: the console changes often, and ours was in Russian, so we describe what each form wants instead of quoting every label. Where the console bit us, there is a warning.

What blocks the submission

For a calling app, these App content forms had to be complete before Play accepted the release for review:

  • Data safety.
  • The foreground service declaration, which needs a link to a demo video.
  • The full-screen intent declaration.
  • The app category.
  • A contact email.

The last three take minutes; the steps below are mostly about the first two. Work in this order: the manifest, then the video, then written Data safety answers, and only then the forms.

Step 1: list what your manifest declares

On Android 14+ the foreground service declaration follows the FOREGROUND_SERVICE_* permissions in your manifest, so start there. During a call, ID Caller keeps a foreground service alive while the app is in the background: microphone for audio, camera for video, and phoneCall because incoming calls go through a self-managed Telecom ConnectionService.

AndroidManifest.xmlxml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />

<!-- Incoming calls via Telecom (self-managed) + FCM, full-screen UI on the lock screen,
     foreground service of an active call -->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />

<!-- ... -->

<!-- Foreground service of an active call: microphone + camera (video) + phoneCall (Telecom) -->
<service
  android:name=".call.CallForegroundService"
  android:exported="false"
  android:foregroundServiceType="phoneCall|microphone|camera" />

Three typed permissions mean three entries to justify in the declaration; USE_FULL_SCREEN_INTENT has a declaration of its own (step 5). Write the list down: it is also the checklist for your video.

The runtime half of the same types

Declaring three types in the manifest does not mean the service can always start with all three. Android 14+ checks the runtime permission behind each type, so we build the type mask from what the user has actually granted:

CallForegroundService.ktkotlin
// Android 14+ checks runtime permissions for the declared types:
// microphone without RECORD_AUDIO and camera without CAMERA throw SecurityException.
// phoneCall (MANAGE_OWN_CALLS is a normal permission) is always allowed.
private fun foregroundServiceTypes(): Int {
    var types = ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
    if (granted(Manifest.permission.RECORD_AUDIO)) {
        types = types or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
    }
    if (granted(Manifest.permission.CAMERA)) {
        types = types or ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
    }
    return types
}

The review did not catch this. Play's crash reports did, after release: 8 crashes across 3 users with exactly this SecurityException.

Step 2: record one demo video, then fill the declaration

The declaration looks light: a few checkboxes and descriptions. The catch: Google wants a video link for every foreground service permission. So the video comes first.

  1. Pick a test device that lets you record the screen over adb (adb shell screenrecord), and check that the resulting file actually plays.
  2. Script the video so it shows the services doing their job in the background. Ours: an incoming video call, accept, press Home (the app goes to the background, the call stays alive), pull down the notification shade with the ongoing-call notification, return to the app, hang up.
  3. Keep it short. Ours runs 50 seconds.
  4. Put the video at a URL the reviewer can open.
  5. In the declaration, tick the camera, microphone and phone call permissions and describe the use: streaming camera and audio while the app is in the background, plus VoIP calling.
  6. Paste the same link for each permission. One video that shows the whole call flow covers all three.
  7. Save the form.

Warning

Without a video link for each permission the declaration does not save. Open it before the video exists, and you can tick the boxes and write the descriptions, but you cannot keep any of it.

Tip

Not every phone can record its own screen. On our realme (ColorOS) the firmware refuses screenrecord with Permission denied, both into /sdcard and into /data/local/tmp. On the Fold in our test fleet it writes an empty file. Honor worked, and Redmi can do it too; we recorded on Honor.

Step 3: write down Data safety answers before opening the form

This is the form that blocked us outright. It said the app collects no data, and the form accepted that without complaint. The problem surfaced only at Google's pre-check before review, which reported the Data safety declaration as invalid and locked the submit button. In reality the app has an identity ID, a push token and AppMetrica analytics.

We do not know which signal the check relies on, and it does not matter: the declaration was wrong. If your calling app has a push token or an analytics SDK, "no data collected" is not true.

The fix was to fill the form from a document rather than from memory. We keep the answers in the repository (docs/store/google-data-safety.md), derived from the privacy policy and from what the server actually stores. For ID Caller they are:

  • Collects user data: yes. Shares it: no.
  • Encrypted in transit: yes. REST and WebSocket go only over TLS, media over DTLS-SRTP.
  • Deletion on request: yes. Deleting the ID in the app (Settings → Account → Delete ID) erases the identity, its links and push tokens on the server; an inactive ID is deleted after 12 months.
  • User IDs: the 12-digit ID Caller ID and its Ed25519 public key, for app functionality.
  • Device or other IDs: the push token (FCM or RuStore) that delivers incoming calls, for app functionality; the internal AppMetrica device id, with no advertising IDs, for analytics.
  • App interactions: sessions and usage events from AppMetrica, for analytics.

Every type is marked as required and not ephemeral. The list of what is not collected matters just as much, each with a reason: no name, email or phone number (there are no accounts), no contacts (the address book is never read), no audio (calls are not recorded), no photos (the QR scanner works on the device), no location (the IP address is used to set up the connection and is not stored).

Note

The answer sheet is a living document. When we added crash reporting through Sentry in September, "App info and performance" moved from not collected to collected, in the same commit as the privacy policy text. Any new SDK means another pass through Data safety.

Step 4: fill the Data safety form strictly forward

With the answers on screen the form takes minutes, as long as you respect two of its habits.

  1. Start with the first question, "Does your app collect or share any of the required user data types?", and answer Yes.
  2. Answer the other overview questions (encryption in transit, data deletion) from your sheet.
  3. Move forward only with Next. Do not jump between steps through the stepper.
  4. On the data type steps, tick every type from your sheet and, for each one, set whether it is collected or shared, required or optional, and its purposes.
  5. Before saving, open the preview and make sure it lists your data types.
  6. Save and go back to the release. Our submit button unlocked once the declaration matched reality.

Warning

Back is not navigation in this form. It rolls the answers you have not saved back to the state stored on the server. Move through the steps with Next and save before you leave.

Warning

A "No" on the first question skips the data type steps (steps 3 and 4 of the form) without warning, and the preview then shows "No data collected". That is exactly the state the pre-check rejected for us. If the preview says it and your app has a push token, start over from the first question.

Step 5: the small forms and the submit

  1. Full-screen intents: declare the calling use case (making and receiving calls), with the permission granted by default.
  2. Category: Communication.
  3. Contact details: email and website. Unlike the declarations, these can be set through the Play Developer API.
  4. Upload the AAB to the internal track first, then create the production release. We went with 177 countries and a full rollout.
  5. Turn on managed publishing if you want to choose the release moment. After Google approves, the release waits on the publishing overview page until someone presses publish; nothing goes live automatically.
  6. Send the release for review.

Note

Every new submission goes through review again, so we collected listing fixes and new builds and sent them together rather than one by one.

Once the forms told the truth, the next release was uneventful: version 1.1 passed Play's pre-check without problems. The only warnings were non-blocking: no R8 mapping file and no native debug symbols.

Checklist

  • Before opening the console, list every FOREGROUND_SERVICE_* permission in your manifest.
  • Record one demo video of the call surviving in the background (test screen recording on the device first) and have its link ready before you open the declaration.
  • Keep the Data safety answers in the repository, derived from the privacy policy and the real data inventory. Push tokens and analytics are collected data.
  • Never treat "No data collected" as a safe default: Play checks it before review.
  • In the Data safety form, move only forward with Next and check the preview before saving. Back discards unsaved answers, and a "No" on the first question skips the data types.
  • Expect the App content forms to be console-only: the API will not fill them.
  • Turn on managed publishing if you want to decide when an approved release goes live.
  • Every new SDK, analytics or crash reporting, means another pass through Data safety.
ShareXLinkedIn

ONLYAPPS Team

Novi Sad, Serbia · onlyapps.org

Contact

We use analytics cookies only with your consent. Essential cookies keep the site working. Privacy policy